/**
 * @author Sandy
 * @version 1.0.0
 * @filename IdGenerator.java
 * @time 2017年3月7日 上午10:21:04
 * @copyright(C) 2017 深圳市互彩通科技有限公司
 */
package com.sky.seata.order.util;

import org.apache.commons.lang.StringUtils;

import java.net.NetworkInterface;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <简述功能> 全局唯一ID生成器
 * <功能详细描述> 生成32位或16位的唯一ID
 * 32位：时间戳(13)+机器码(8)+线程ID(4)+随机数(8)
 * 24位：yyMMddHHmmss(12)+线程ID(4)+随机数(8)
 * 16位：前缀(1)+yyMMdd(6)+线程ID(4)+随机数(5)
 *
 * @see
 * @since
 */

public class IdGenerator {
    /**
     * 机器码
     */
    private static final int MACHINE_IDENTIFIER;
    private static final short PROCESS_IDENTIFIER;
    private static final AtomicInteger NEXT_COUNTER = new AtomicInteger(new SecureRandom().nextInt());
    private static final AtomicInteger NEXT_COUNTER_5 = new AtomicInteger(new SecureRandom().nextInt(99999));
    private static final AtomicInteger NEXT_COUNTER_9 = new AtomicInteger(new SecureRandom().nextInt(999999999));

    private static final int LOW_ORDER_THREE_BYTES = 0x00ffffff;
//	private static final int LOW_ORDER_BYTES_FOR_8 = 0x00ffffff;
//	private static final int LOW_ORDER_BYTES_FOR_5 = 0x0000ffff;

    static {
        try {
            MACHINE_IDENTIFIER = createMachineIdentifier();
            PROCESS_IDENTIFIER = createProcessIdentifier();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private final long timestamp;
    private final int machineIdentifier;
    private final short processIdentifier;
    private final int counter;


    private IdGenerator(final long timestamp, final int machineIdentifier, final short processIdentifier, final int counter
            , final int lowOrderBytes, final boolean checkCounter) {
        if ((machineIdentifier & 0xff000000) != 0) {
            throw new IllegalArgumentException("The machine identifier must be between 0 and 16777215 (it must fit in three bytes).");
        }
        if (checkCounter && ((counter & 0xff000000) != 0)) {
            throw new IllegalArgumentException("The counter must be between 0 and 16777215 (it must fit in three bytes).");
        }
        this.timestamp = timestamp;
        this.machineIdentifier = machineIdentifier;
        this.processIdentifier = processIdentifier;
        this.counter = counter & lowOrderBytes;
    }

    public static String GenerateId32() {
        IdGenerator id = new IdGenerator(Calendar.getInstance().getTime().getTime(), MACHINE_IDENTIFIER, PROCESS_IDENTIFIER, NEXT_COUNTER.getAndIncrement(), LOW_ORDER_THREE_BYTES, false);
        StringBuilder sb = new StringBuilder().append(id.timestamp).append(id.machineIdentifier).append(id.processIdentifier).append(id.counter);
        if (sb.length() > 32) {
            sb.delete(32, sb.length());
        }
        return sb.toString();
    }

    /**
     * 生成28位随机数
     * yyMMddHHmmssSSS(15)+线程ID(1)+随机数(8)
     *
     * @return
     */
    public static String GenerateId28() {
        IdGenerator id = new IdGenerator(Calendar.getInstance().getTime().getTime(), MACHINE_IDENTIFIER, PROCESS_IDENTIFIER, NEXT_COUNTER.getAndIncrement(), LOW_ORDER_THREE_BYTES, false);

        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String date = formatter.format(Calendar.getInstance().getTime());
        StringBuilder sb = new StringBuilder(date);
        sb.append(id.counter);
        sb.append(id.machineIdentifier);
        if (sb.length() > 28) {
            sb.delete(28, sb.length());
        }
        return sb.toString();
    }

    /**
     * 生成24位随机数
     * yyMMddHHmmssSSS(15)+线程ID(1)+随机数(8)
     *
     * @return
     */
    public static String GenerateId24() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyMMddHHmmssSSS");
        String date = formatter.format(Calendar.getInstance().getTime());
        StringBuilder sb = new StringBuilder(date);
        int pid = PROCESS_IDENTIFIER / 1000;
        sb.append(pid);
        int random = NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES;
        if (random / 10000000 == 0) {
            sb.append(0).append(random);
        } else {
            sb.append(random);
        }

        if (sb.length() > 24) {
            sb.delete(24, sb.length());
        }
        return sb.toString();
    }

    /**
     * 生成24位订单Id
     * yyMMddHHmmssSSS(15)+线程ID(1)+随机数(8)
     *
     * @return
     */
    public static String GenerateNetOrderId() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
        String date = formatter.format(Calendar.getInstance().getTime());
        StringBuilder sb = new StringBuilder();
        sb.append("D").append(date);
        int pid = PROCESS_IDENTIFIER / 1000;
        sb.append(pid);
        int random = NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES;
        if (random / 10000000 == 0) {
            sb.append(0).append(random);
        } else {
            sb.append(random);
        }

        if (sb.length() > 24) {
            sb.delete(24, sb.length());
        }
        return sb.toString();
    }

    /**
     * 生成18位数的ID
     * yyMMddHHmmssSSS(15) + 随机数(8)
     *
     * @return
     */
    public static String GenerateId23() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyMMddHHmmssSSS");
        String date = formatter.format(Calendar.getInstance().getTime());
        StringBuilder sb = new StringBuilder(date);
        int random = NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES;
        if (random / 10000000 == 0) {
            sb.append(0).append(random);
        } else {
            sb.append(random);
        }

        if (sb.length() > 23) {
            sb.delete(23, sb.length());
        }

        return sb.toString();
    }

    /**
     * 生成20位随机数
     * yyMMddHHmmss(12)+随机数(8)
     *
     * @return
     */
    public static String GenerateId20() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyMMddHHmmss");
        String date = formatter.format(Calendar.getInstance().getTime());
        StringBuilder sb = new StringBuilder(date);
        int random = NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES;
        if (random / 10000000 == 0) {
            sb.append(0).append(random);
        } else {
            sb.append(random);
        }

        if (sb.length() > 20) {
            sb.delete(20, sb.length());
        }
        return sb.toString();
    }


    /**
     * 生成13位随机数
     * 随机字符串(5)+随机数(8)
     *
     * @return
     */
    public static String GenerateId13() {
        String ram = getRandomStringByLength(5);
        StringBuilder sb = new StringBuilder(ram);
        int random = NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES;
        if (random / 10000000 == 0) {
            sb.append(0).append(random);
        } else {
            sb.append(random);
        }

        if (sb.length() > 13) {
            String str = sb.delete(13, sb.length()).toString();
            return str;
        } else if (sb.length() < 13) {
            String str = StringUtils.rightPad(sb.toString(), sb.length() - 13, "0");
            return str;
        }
        return sb.toString();
    }

    public static String GenerateMchAccId16(int pref) {
        if (pref > 99 || pref < 10) {
            /**@todo 这里是否要抛出异常*/
//			throw new OutMaxLanthException();
        }
//		IdGenerator id = new IdGenerator(pref, MACHINE_IDENTIFIER, PROCESS_IDENTIFIER, NEXT_COUNTER.getAndIncrement(), LOW_ORDER_BYTES_FOR_4, false);
        SimpleDateFormat formatter = new SimpleDateFormat("yyMMddHHmmss");
        String date = formatter.format(Calendar.getInstance().getTime());
        int random = NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES;
        String ramStr = String.valueOf(random);
        if (ramStr.length() > 2) {
            ramStr = ramStr.substring(ramStr.length() - 2, ramStr.length());
        } else {
            Random r = new Random();
            ramStr = "" + r.nextInt(10) + r.nextInt(10);
        }

        return new StringBuilder().append(pref).append(date).append(PROCESS_IDENTIFIER).append(ramStr).toString();
    }


    /**
     * 获取一定长度的随机字符串
     *
     * @param length 指定字符串长度
     * @return 一定长度的字符串
     */
    public static String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }


    private static int createMachineIdentifier() {
        // build a 2-byte machine piece based on NICs info
        int machinePiece;
        try {
            StringBuilder sb = new StringBuilder();
            Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
            while (e.hasMoreElements()) {
                NetworkInterface ni = e.nextElement();
                sb.append(ni.toString());
                byte[] mac = ni.getHardwareAddress();
                if (mac != null) {
                    ByteBuffer bb = ByteBuffer.wrap(mac);
                    try {
                        sb.append(bb.getChar());
                        sb.append(bb.getChar());
                        sb.append(bb.getChar());
                    } catch (BufferUnderflowException shortHardwareAddressException) { // NOPMD
                        // mac with less than 6 bytes. continue
                    }
                }
            }
            machinePiece = sb.toString().hashCode();
        } catch (Throwable t) {
            // exception sometimes happens with IBM JVM, use random
            machinePiece = (new SecureRandom().nextInt());
        }
        machinePiece = machinePiece & LOW_ORDER_THREE_BYTES;
        return machinePiece;
    }

    /**
     * @todo 需要改动
     */
    private static short createProcessIdentifier() {
        short processId;
        try {
            String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
            if (processName.contains("@")) {
                String processNameNew = processName.substring(0, processName.indexOf('@'));
                if (processNameNew.length() > 4) {
                    processNameNew = processNameNew.substring(processNameNew.length() - 4, processNameNew.length());
                    if (processNameNew.startsWith("0")) {
                        processNameNew = processNameNew.replaceFirst("0", "1");
                    }
                    processId = (short) Integer.parseInt(processNameNew);
                } else if (processNameNew.length() < 4) {
                    processId = (short) Integer.parseInt(StringUtils.leftPad(processNameNew, 4, "123"));
                } else {
                    processId = (short) Integer.parseInt(processNameNew);
                }
            } else {
                processId = (short) java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode();
            }

        } catch (Throwable t) {
            processId = (short) new SecureRandom().nextInt(9999);
        }

        return processId;
    }

    /***
     * 生成uid 数字
     */
    public static String generateUID(int length) {
        Random random = new Random();
        String result = "";
        for (int i = 0; i < length; i++) {
            //首字母不能为0
            if (i == 0) {
                result += (random.nextInt(9) + 1);
            } else {
                result += (random.nextInt(9));
            }
        }
        return result;
    }

    private static int ranInt(int min, int max) {
        Random random = new Random();
        int s = random.nextInt(max) % (max - min + 1) + min;
        return s;
    }

    public static void main(String[] args) {
        System.out.println(GenerateId24());
    }

}
